#include "raster.h"
#include "raster-io.h"

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#ifdef _MSC_VER
#include <malloc.h> /* alloca */
#pragma warning( disable : 4996 ) /* function unsafe */
#endif

int TranslateFromRGB24(Pixel *dest, unsigned char *src, int num)
{
    unsigned char *ccdest = (unsigned char *)dest;

    while(num > 0)
    {
        *ccdest++ = *src++;
        *ccdest++ = *src++;
        *ccdest++ = *src++;
        *ccdest++ = 0xFF;
        num--;
    }
    return 0;
}


typedef struct
{
    unsigned int hdrSize;
    int width;
    int height;
    unsigned short colorPlanes;
    unsigned short bitsPerPel;
    unsigned int compression;
    unsigned int imgSize;
    int hRes;
    int vRes;
    unsigned int paletteSize;
    unsigned int numImportantColors;
} _BitmapInfoHeader;

#define BMP_RGB 0
#define BMP_BITFIELDS 3
#define BMP_ALPHABITFIELDS 6

static Raster *_RasterFromBitmap(FILE *fp)
{
    Raster *ret = NULL;
    _BitmapInfoHeader header;
    unsigned int offset;

    fseek(fp, 8, SEEK_CUR); /* skip file size and reserved bytes */
    fread(&offset, 4, 1, fp); /* offset to pixels */
    fread(&header, sizeof(_BitmapInfoHeader), 1, fp);

    if(header.compression == BMP_RGB) /* flat storage in either R8G8B8 or X8R8G8B8 format */
    {
        ret = CreateRaster(FORMAT_FLAT_XRGB32, header.width, header.height);
        if(!ret)
            return NULL;

        fseek(fp, offset, SEEK_SET); /* skip to pixels */
        if(header.bitsPerPel == 32) /* pixels already in raster format */
        {
            /* bitmaps are stored upside-down */
            int y = 1;
            while(y <= header.height)
            {
                fread(ret->pixels + ((header.height - y) * ret->pitch), 4, header.width, fp);
                y++;
            }
        }
        else if(header.bitsPerPel == 24)
        {
            int y = 1;
            /*  all bitmap rows are padded to 4 bytes
                for 24-bit images that aren't a multiple of 4px
                wide, this means the row ends with padding
                formula taken from wikipedia:
                    http://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage
            */
            int rowSize = (((24 * header.width) + 31) / 32) * 4;
            /* use of alloca here lead to invalidation of the header
                very odd bug
            */
            unsigned char *buffer = (unsigned char *)alloca(rowSize);
            while(y <= header.height)
            {
                fread(buffer, 1, rowSize, fp);
                TranslateFromRGB24(ret->pixels + ((header.height-y) * ret->pitch), buffer, header.width);
                y++;
            }
        }
        else
        {
            /* error */
            DestroyRaster(ret);
            return NULL;
        }
        return ret;
    }
    return ret;
}

Raster * __cdecl RasterFromFile(const char *fname)
{
    FILE *fp = fopen(fname, "rb");
    Raster *ret = NULL;
    if(!fp)
        return NULL;
    /* bitmap check */
    if(fgetc(fp) == 'B' && fgetc(fp) == 'M')
    {
        ret = _RasterFromBitmap(fp);
        fclose(fp);
    }
    return ret;
}
